package cmd

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	"github.com/gogf/gf/v2/container/garray"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/encoding/gjson"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gcmd"
	"github.com/gogf/gf/v2/os/gview"
	"github.com/gogf/gf/v2/text/gregex"
	"github.com/gogf/gf/v2/text/gstr"
)

type GenTableField struct {
	// 字段名
	FieldName string
	// 字段注释
	Remark string
	// 字段类型
	FieldType string
	// 大驼峰字段名
	FieldCamelName string
	// 小驼峰表名
	FieldCamelNameLower string
	// go语言的数据类型
	GoType string
	// 是否为空
	Null bool
}
type GenTable struct {
	// 模块名
	ModuleName string
	// 表名-去掉模块名的表名
	TableName string
	// 真实表名
	RealTableName string
	// 小驼峰表名
	TableCamelNameLower string
	// 大驼峰表名
	TableCamelName string
	// 表注释
	Remark      string
	TableFieles []GenTableField
	// 是否包含时间字段
	HasTime bool
	// 是否包含逻辑删除字段
	HasIsDeleted bool
	// dto是否包含时间字段
	DtoHasTime bool
	// 是否为树结构
	IsTree bool
	// 是否包含排序字段
	HasSort       bool
	HasCreateTime bool
	HasUpdateTime bool
}

// renderTemplate 渲染模板并写入文件
func renderTemplate(ctx context.Context, view *gview.View, templateName, fileNamePath string, data map[string]interface{}, tableCover bool) error {
	// 渲染模板
	resultStr, err := view.Parse(ctx, templateName, data)
	if err != nil {
		return fmt.Errorf("渲染模板失败: %v", err)
	}
	// fmt.Println(templateName, ":", resultStr)

	// 获取输出路径
	outPath, err := g.Cfg().Get(ctx, "gen.outPath")
	if err != nil {
		return fmt.Errorf("获取配置失败: %v", err)
	}

	// 定义文件路径
	filePath := filepath.Join(outPath.String(), fileNamePath)

	// 创建目录（如果不存在）
	dir := filepath.Dir(filePath)
	if err := os.MkdirAll(dir, os.ModePerm); err != nil {
		return fmt.Errorf("创建目录失败: %v", err)
	}
	if tableCover {
		// 将内容写入文件
		if err := os.WriteFile(filePath, []byte(resultStr), 0644); err != nil {
			return fmt.Errorf("写入文件失败: %v", err)
		}
		fmt.Println("文件已成功写入:", filePath)
	} else {
		fmt.Println("文件已存在", filePath)
	}

	return nil
}

var (
	Gen = gcmd.Command{
		Name:  "gen",
		Usage: "gen -t sys_user\n\tgen -t sys_role -c 1\n\tgen -t sys_role -m sys",
		Brief: "代码生成工具",
		Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
			moduleName := ""
			cover := parser.GetOpt("c")
			tableCover := cover != nil && cover.Bool()
			if tableCover {
				fmt.Println("覆盖已存在的文件")
			}
			table := parser.GetOpt("t")
			if table == nil || table.String() == "" {
				fmt.Println("请输入表名")
				return
			}
			tableName := table.String()
			// 处理模块名
			moduleNameOpt := parser.GetOpt("m")
			if moduleNameOpt != nil && moduleNameOpt.String() != "" {
				moduleName = moduleNameOpt.String()
			} else {
				// 通过表名称自动提取，即sys_user=>sys,cms_category=>cms
				moduleName = gstr.Split(tableName, "_")[0]
			}
			// 获取所有表
			tables, err := g.DB().Tables(ctx)
			if err != nil {
				fmt.Println("获取所有表失败:", err)
				return
			}
			// 校验表是否存在
			if !garray.NewStrArrayFrom(tables).Contains(tableName) {
				fmt.Println("表不存在")
				return
			}
			// 获取表注释
			dbName := g.DB().GetConfig().Name // 获取数据库名
			query := fmt.Sprintf("SELECT TABLE_COMMENT FROM information_schema.TABLES WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s'", dbName, tableName)
			result, err := g.DB().GetAll(ctx, query)
			if err != nil {
				fmt.Println("获取表注释失败:", err)
				return
			}
			tableComment := result[0]["TABLE_COMMENT"]
			if tableComment == nil || tableComment.String() == "" {
				fmt.Println("表注释不能为空")
				return
			}
			fmt.Println("表注释:", tableComment)
			// 获取表字段信息
			tableFieles, err := g.DB().TableFields(ctx, tableName)
			if err != nil {
				fmt.Println("获取表结构信息失败:", err)
				return
			}
			// 获取字段原始顺序（保持不变）
			ordinalQuery := fmt.Sprintf(`SELECT COLUMN_NAME FROM information_schema.COLUMNS 
			WHERE TABLE_SCHEMA = '%s' AND TABLE_NAME = '%s' 
			ORDER BY ORDINAL_POSITION`, dbName, tableName)
			ordinals, _ := g.DB().GetAll(ctx, ordinalQuery)

			// 按原始顺序收集字段（改用切片）
			orderedFields := make([]*gdb.TableField, 0, len(tableFieles))
			for _, item := range ordinals {
				name := item["COLUMN_NAME"].String()
				if field, exists := tableFieles[name]; exists {
					orderedFields = append(orderedFields, field)
				}
			}
			// 组装GenTable对象
			genTable := GenTable{RealTableName: tableName, Remark: tableComment.String()}
			// 设置是否包含时间字段
			genTable.HasTime = false
			genTable.HasIsDeleted = false
			genTable.DtoHasTime = false
			genTable.IsTree = false
			genTable.HasSort = false
			genTable.HasCreateTime = false
			genTable.HasUpdateTime = false
			// 设置模块名
			genTable.ModuleName = moduleName
			// 替换前辍的表名
			genTable.TableName = gstr.Replace(tableName, moduleName+"_", "")
			// 设置TableCamelName,其为tableName驼峰命名规则，且首字母大写
			genTable.TableCamelName = gstr.CaseCamel(gstr.Replace(tableName, moduleName+"_", ""))
			// 设置TableCamelNameLower
			genTable.TableCamelNameLower = gstr.CaseCamelLower(gstr.Replace(tableName, moduleName+"_", ""))
			// 从配置文件中获取 dataTypeMap
			dataTypeMap, err := g.Cfg().Get(ctx, "gen.dataTypeMap", map[string]string{})
			if err != nil {
				return nil
			}
			// 将 dataTypeMap 转换为 map[string]string 类型
			dataTypeMapStr := dataTypeMap.MapStrStr()
			for _, v := range orderedFields {
				// 组装GenTableField对象
				genTableField := GenTableField{FieldName: v.Name, FieldType: v.Type, Remark: v.Comment}
				genTableField.Null = v.Null
				if genTableField.FieldName == "id" {
					genTableField.FieldCamelName = "ID"
					genTableField.FieldCamelNameLower = "id"
				} else {
					genTableField.FieldCamelName = gstr.CaseCamel(genTableField.FieldName)
					genTableField.FieldCamelNameLower = gstr.CaseCamelLower(genTableField.FieldName)
				}
				// 存在parent_id字段，则为树结构
				if genTableField.FieldName == "parent_id" {
					genTable.IsTree = true
				}
				if genTableField.FieldName == "create_time" {
					genTable.HasCreateTime = true
				}
				if genTableField.FieldName == "update_time" {
					genTable.HasUpdateTime = true
				}
				// 优化一下优化，genTableField.GoType的类型从配置文件的gen.dataTypeMap中获取，key为genTableField.FieldType，如何找不到，则使用string
				// 使用正则匹配varchar(111),bigint等
				match, _ := gregex.MatchString(`^([\w]+)(\((\d+)\))?$`, genTableField.FieldType)
				if len(match) > 1 {
					genTableField.FieldType = match[1]
				}
				if genTableField.FieldName == "is_deleted" {
					genTable.HasIsDeleted = true
				}
				if genTableField.FieldName == "sort" {
					genTable.HasSort = true
				}
				// 修改 gen.go 中字段类型判断逻辑
				switch strings.ToLower(genTableField.FieldType) {
				case "date", "datetime", "timestamp":
					genTableField.GoType = "*gtime.Time"
					genTable.HasTime = true
					// 如果不是create_time/create_user/update_time/update_user/is_deleted字段,则DtoHasTime=true
					if !garray.NewStrArrayFrom([]string{"create_time", "create_user", "update_time", "update_user", "is_deleted"}).Contains(genTableField.FieldName) {
						genTable.DtoHasTime = true
					}
				default:
					if goType, ok := dataTypeMapStr[genTableField.FieldType]; ok {
						genTableField.GoType = goType
					} else {
						genTableField.GoType = "string"
					}
				}
				genTable.TableFieles = append(genTable.TableFieles, genTableField)
			}
			// 使用 gjson.New 创建 JSON 对象
			jsonObj := gjson.New(genTable)
			// 使用 Indent 方法格式化 JSON 字符串
			// jsonStrFormatted := jsonObj.MustToJsonIndentString()
			// fmt.Println("格式化后的 GenTable JSON 字符串:\n", jsonStrFormatted)
			// 实例化 g.View()
			view := g.View()
			// 设置模板路径
			view.SetPath("resource/template/gen")
			// 获取模板配置
			templates, err := g.Cfg().Get(ctx, "gen.templates")
			if err != nil {
				fmt.Println("获取模板配置失败:", err)
				return
			}
			copyTableCover := tableCover
			for _, b := range templates.Array() {
				t := b.(map[string]interface{})
				onlyRelTable := t["onlyRelTable"].(bool)
				if strings.Contains(genTable.Remark, "r_") && !onlyRelTable {
					continue
				}
				tableCover = copyTableCover
				fileOverride := t["fileOverride"].(bool)
				if !tableCover {
					// 命令行优先，没有才读取配置
					tableCover = fileOverride
				}

				templatePath := t["templatePath"].(string)
				templateTarget := t["templateTarget"].(string)
				templatePath, err = view.ParseContent(ctx, templatePath, jsonObj.Map())
				if err != nil {
					fmt.Println("解析模板失败:", err)
					return
				}
				templateTarget, err = view.ParseContent(ctx, templateTarget, jsonObj.Map())
				if err != nil {
					fmt.Println("解析模板失败:", err)
					return
				}
				renderTemplate(ctx, view, templatePath, templateTarget, jsonObj.Map(), tableCover)
			}
			return nil
		},
	}
)
